UNPKG

cejs

Version:

A JavaScript module framework that is simple to use.

373 lines (317 loc) 13.6 kB
/** * @name CeL function for MediaWiki (Wikipedia / 維基百科): 管理員相關的 adminship * functions * * @fileoverview 本檔案包含了 MediaWiki 自動化作業用程式庫的子程式庫。 * * TODO:<code> </code> * * @since 2019/10/12 拆分自 CeL.application.net.wiki */ // More examples: see /_test suite/test.js // Wikipedia bots demo: https://github.com/kanasimi/wikibot 'use strict'; // 'use asm'; // -------------------------------------------------------------------------------------------- // 不採用 if 陳述式,可以避免 Eclipse JSDoc 與 format 多縮排一層。 typeof CeL === 'function' && CeL.run({ // module name name : 'application.net.wiki.admin', require : 'application.net.wiki.' // load MediaWiki module basic functions + '|application.net.wiki.namespace.' // + '|application.net.wiki.query.|application.net.wiki.page.', // 設定不匯出的子函式。 no_extend : 'this,*', // 為了方便格式化程式碼,因此將 module 函式主體另外抽出。 code : module_code }); function module_code(library_namespace) { // requiring var wiki_API = library_namespace.application.net.wiki, KEY_SESSION = wiki_API.KEY_SESSION; // ------------------------------------------------------------------------ // administrator functions. 管理員相關函數。 // 自 options 汲取出 parameters。 // TODO: 整合進 normalize_parameters。 // default_parameters[parameter name] = required function draw_parameters(options, default_parameters, token_type) { if (!options) { // Invalid options/parameters return 'No options specified'; } // 汲取出 parameters。 var parameters = Object.create(null); if (default_parameters) { for ( var parameter_name in default_parameters) { if (parameter_name in options) { // in case options[parameter_name] === false if (options[parameter_name]) parameters[parameter_name] = options[parameter_name]; } else if (default_parameters[parameter_name]) { // 表示此屬性為必須存在/指定的屬性。 // This parameter is required. return 'No property ' + parameter_name + ' specified'; } } } var session = options[KEY_SESSION]; // assert: 有parameters, e.g., {Object}parameters // 可能沒有 session // ---------------------------- // 處理 target page。 var default_KEY_ID = 'pageid', default_KEY_TITLE = 'title', KEY_ID = default_KEY_ID, KEY_TITLE = default_KEY_TITLE; if (parameters.to) { // move_to KEY_ID = 'fromid'; KEY_TITLE = 'from'; } // 都先從 options 取值,再從 session 取值。 if (options[KEY_ID] >= 0 || options[default_KEY_ID] >= 0) { parameters[KEY_ID] = options[KEY_ID] >= 0 ? options[KEY_ID] : options[default_KEY_ID]; } else if (options[KEY_TITLE] || options[default_KEY_TITLE]) { parameters[KEY_TITLE] = wiki_API.title_of(options[KEY_TITLE] // options.from_title || options[default_KEY_TITLE]); } else if (wiki_API.is_page_data(session && session.last_page)) { // options.page_data if (session.last_page.pageid >= 0) parameters[KEY_ID] = session.last_page.pageid; else parameters[KEY_TITLE] = session.last_page.title; } else { // 可能沒有 page_data if (library_namespace.is_debug()) { library_namespace.error('draw_parameters: No page specified: ' + JSON.stringify(options)); } return 'No page id/title specified'; } // ---------------------------- // 處理 token。 if (!token_type) { token_type = 'csrf'; } var token = options.token || session && session.token; if (token && typeof token === 'object') { // session.token.csrftoken token = token[token_type + 'token']; } if (!token) { // TODO: use session if (false) { library_namespace.error('draw_parameters: No token specified: ' + options); } return 'No ' + token_type + 'token specified'; } parameters.token = token; return parameters; } // use "csrf" token retrieved from action=query&meta=tokens // callback(response, error); function wiki_operator(action, default_parameters, options, callback) { // default_parameters // Warning: 除 pageid/title/token 之外,這邊只要是能指定給 API 的,皆必須列入! var parameters = draw_parameters(options, default_parameters); // console.log(parameters); if (!library_namespace.is_Object(parameters)) { // error occurred. if (typeof callback === 'function') callback(undefined, parameters); return; } // TODO: 若是頁面不存在/已刪除,那就直接跳出。 if (action === 'move') { library_namespace.is_debug((parameters.fromid || parameters.from) // .move_to_title + ' → ' + parameters.to, 1, 'wiki_operator.move'); } wiki_API.query({ action : action }, function(response, error) { // console.log(JSON.stringify(response)); if (wiki_API.query.handle_error(response, error, callback)) { return; } callback(response[action]); }, parameters, options); } // ================================================================================================================ // wiki_API.delete(): remove / delete a page. wiki_API['delete'] = function(options, callback) { // https://www.mediawiki.org/w/api.php?action=help&modules=delete /** * response: <code> {"delete":{"title":"Title","reason":"content was: \"...\", and the only contributor was \"[[Special:Contributions/Cewbot|Cewbot]]\" ([[User talk:Cewbot|talk]])","logid":0000}} {"error":{"code":"nosuchpageid","info":"There is no page with ID 0.","*":"See https://test.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at &lt;https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&gt; for notice of API deprecations and breaking changes."},"servedby":"mw1232"} * </code> */ wiki_operator('delete', { reason : false, tags : false, watchlist : false, watchlistexpiry : false, oldimage : false }, options, callback); }; // ---------------------------------------------------- // wiki_API.move_to(): move a page from `from` to target `to`. wiki_API.move_to = function(options, callback) { // https://www.mediawiki.org/w/api.php?action=help&modules=move var default_parameters = { to : true, reason : false, movetalk : false, movesubpages : false, noredirect : false, watchlist : false, ignorewarnings : false, tags : false }; if (!options || !options.reason) { library_namespace .warn('wiki_API.move_to: Should set reason when moving page!'); } /** * response: <code> {"error":{"code":"nosuchpageid","info":"There is no page with ID 0.","*":"See https://zh.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at &lt;https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&gt; for notice of API deprecations and breaking changes."},"servedby":"mw1277"} error: {"code":"articleexists","info":"A page of that name already exists, or the name you have chosen is not valid. Please choose another name.","*":"See https://zh.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at &lt;https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&gt; for notice of API deprecations and breaking changes."} {"code":"selfmove","info":"The title is the same; cannot move a page over itself.","*":"See https://zh.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at &lt;https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&gt; for notice of API deprecations and breaking changes."} { from: 'from', to: 'to', reason: '', redirectcreated: '', moveoverredirect: '' } { error: { code: 'articleexists', info: 'A page already exists at [[:To]], or the page name you have chosen is not valid. Please choose another name.', '*': 'See https://test.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at &lt;https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&gt; for notice of API deprecations and breaking changes.' } } </code> */ // console.log(options); wiki_operator('move', default_parameters, options, callback); }; // ---------------------------------------------------- // @see wiki_API.is_protected // Change the protection level of a page. wiki_API.protect = function(options, callback) { // https://www.mediawiki.org/w/api.php?action=help&modules=protect /** * response: <code> {"protect":{"title":"title","reason":"存檔保護作業","protections":[{"edit":"sysop","expiry":"infinite"},{"move":"sysop","expiry":"infinite"}]}} {"servedby":"mw1203","error":{"code":"nosuchpageid","info":"There is no page with ID 2006","*":"See https://zh.wikinews.org/w/api.php for API usage"}} * </code> */ wiki_operator('protect', { // protections: e.g., 'edit=sysop|move=sysop', 一般說來edit應與move同步。 protections : true, // 在正式場合,最好給個好的理由。 // reason: @see [[MediaWiki:Protect-dropdown]] reason : false, // expiry : 'infinite', expiry : false, tags : false, cascade : false, watchlist : false }, Object.assign({ protections : 'edit=sysop|move=sysop' }, options), callback); }; // ---------------------------------------------------- // rollback 僅能快速撤銷/回退/還原某一頁面最新版本之作者(最後一位使用者)一系列所有編輯至另一作者的編輯 // The rollback revision will be marked as minor. wiki_API.rollback = function(options, callback) { var session = wiki_API.session_of_options(options); if (session && !session.token.rollbacktoken) { session.get_token(function() { wiki_API.rollback(options, callback); }, 'rollback'); } var parameters = draw_parameters(options, { // default_parameters // Warning: 除外pageid/title/token這邊只要是能指定給 API 的,皆必須列入! user : false, summary : false, markbot : false, tags : false }, 'rollback'); if (!library_namespace.is_Object(parameters)) { // error occurred. if (typeof callback === 'function') callback(undefined, parameters); return; } // 都先從 options 取值,再從 session 取值。 var page_data = // options.page_data || options.pageid && options || session && session.last_page; // assert: 有parameters, e.g., {Object}parameters // 可能沒有 session, page_data if (!parameters.user && wiki_API.content_of.revision(page_data)) { // 將最後一個版本的編輯者當作回退對象。 parameters.user = wiki_API.content_of.revision(page_data).user; } // https://www.mediawiki.org/w/api.php?action=help&modules=rollback // If the last user who edited the page made multiple edits in a row, // they will all be rolled back. if (!parameters.user) { // 抓最後的編輯者試試。 // 要用pageid的話,得採page_data,就必須保證兩者相同。 if (!parameters.title && page_data && parameters.pageid !== page_data.pageid) { callback(undefined, 'parameters.pageid !== page_data.pageid'); return; } wiki_API.page(page_data || parameters.title, function(page_data, error) { if (error || !wiki_API.content_of.revision(page_data) // 保證不會再持續執行。 || !wiki_API.content_of.revision(page_data).user) { if (false) { library_namespace.error( // 'wiki_API.rollback: No user name specified!'); } callback(undefined, // 'No user name specified and I cannot guess it!'); return; } wiki_API.rollback(options, callback); }, Object.assign({ rvprop : 'ids|timestamp|user' }, options)); return; } if (!('markbot' in parameters) && options.bot) { parameters.markbot = options.bot; } /** * response: <code> {"rollback":{"title":"title","pageid":1,"summary":"","revid":9,"old_revid":7,"last_revid":1,"messageHtml":"<p></p>"}} {"servedby":"mw1190","error":{"code":"badtoken","info":"Invalid token","*":"See https://zh.wikinews.org/w/api.php for API usage"}} * </code> */ wiki_API.query({ action : 'rollback' }, function(response) { var error = response && response.error; if (error) { callback(response, error); } else { // revid 回滾的版本ID。 // old_revid 被回滾的第一個(最新)修訂的修訂ID。 // last_revid 被回滾最後一個(最舊)版本的修訂ID。 // 如果回滾不會改變的頁面,沒有新修訂而成。在這種情況下,revid將等於old_revid。 callback(response.rollback); } }, parameters, options); }; // ---------------------------------------------------- // 目前的修訂,不可隱藏。 // This is the current revision. It cannot be hidden. wiki_API.hide = function(options, callback) { TODO; }; // ------------------------------------------------------------------------ // export 導出. // 不設定(hook)本 module 之 namespace,僅執行 module code。 return library_namespace.env.not_to_extend_keyword; }