コンテンツにスキップ

モジュール:Category handler

提供: Wikisource
モジュールの解説[表示] [編集] [履歴] [キャッシュを破棄]

このモジュールは {{category handler}} テンプレートを実装しています。category handler テンプレートは、他のテンプレートがカテゴリ分けと カテゴリ抑制 の両方を自動化するのに役立ちます。他のテンプレート内でこのテンプレートを使用する方法については、テンプレートのドキュメントを参照してください。Lua モジュール内でこのモジュールを使用する方法や、他のウィキへエクスポートする方法については、以下を読み進めてください。

他の Lua モジュールからの使用

[編集]

このモジュールを使用すべきでない場合

[編集]

モジュールがメイン(記事)、ファイル(画像)、カテゴリのいずれかの名前空間でしかカテゴリ化する必要がない場合、このモジュールを使うのはやりすぎです。代わりに mw.title.getCurrentTitle を使ってタイトルオブジェクトを取得し、nsText フィールドをチェックすれば十分です。例:

local title = mw.title.getCurrentTitle()
if title.nsText == 'File' then
    -- ここに処理を書く
end

しかし、他の名前空間でカテゴリ化する必要がある場合は、このモジュールを使用することを推奨します。このモジュールはカテゴリ抑制に正しく対応しており、名前空間ごとにどのようにカテゴリ化するかを簡単に指定できます。

名前空間

[編集]

このモジュールは、Wikipedia で使用されるさまざまな名前空間を検出し、いくつかのタイプに分類します。これらのタイプは、このモジュール内でパラメータ名として使用されます。

main = メイン/記事名前空間(通常のWikipedia記事など)
talk = あらゆるトーク名前空間(例:"トーク:"、"利用者・トーク:"、"ファイル・トーク:" などで始まるページ)
user, wikipedia, file ... = 議論ページ以外のその他の名前空間。エイリアスも使用可能です。下記の表で一覧をご覧ください。
other = テンプレートにパラメータとして指定されていないその他の名前空間
指定可能な名前空間パラメータ一覧

talk および other を除く)

名前空間 エイリアス
main
利用者 user
wikisource project, ws
ファイル file, 画像, image
mediawiki
テンプレート template
ヘルプ help
カテゴリ category
作者
page
index
timedtext
モジュール module

基本的な使い方

[編集]

このモジュールは、2つ以上のパラメータを取ります。以下は「Hello world」プログラムの例です:

p = {}
local categoryHandler = require( 'Module:Category handler' ).main

function p.main( frame )
    local result = 'Hello world!'
    local category = categoryHandler{
        '[[Category:Somecat]]',
        nocat = frame.args.nocat -- "nocat=true/false" に対応
    }
    category = category or '' -- category が nil でないことを確認
    return result .. category
end

return p

この例では、category handler モジュールのデフォルト設定を使用しています。つまり、以下の名前空間でカテゴリ分けされます:

メインファイルヘルプカテゴリーポータルブック

以下の名前空間ではカテゴリ分けされません

トーク利用者wikipediamediawikiテンプレート など

また、ブラックリストに登録されたページでもカテゴリ分けされません(詳細はブラックリストを参照)。

category handler モジュールが一部の名前空間でカテゴリ分けしない理由は、それらの名前空間では、モジュールやテンプレートが主に使用されるのではなく、一覧表示や例示のために存在することが多いためです。

モジュールやテンプレートが、カテゴリ分けが有効な名前空間で使用されることを意図している場合には、この基本的な構文を使うことで十分です。

応用的な使い方

[編集]

このモジュールは、前述の名前空間のセクションにある各ページタイプ名をパラメータ名とする、1つ以上のパラメータを受け取ります。これらのパラメータを使うことで、テンプレートがどの名前空間でカテゴリ分けを行うかを正確に指定できます。以下のように記述します:

p = {}
local categoryHandler = require( 'Module:Category handler' ).main

function p.main( frame )
    local result = 'この記事と議論ページで使用されるモジュールです。'
    local category = categoryHandler{
        main = '[[Category:Somecat1]]', -- 記事空間でカテゴリ分け
        talk = '[[Category:Somecat2]]', -- トーク空間でカテゴリ分け
        nocat = frame.args.nocat -- "nocat=true/false" に対応
    }
    category = category or ''
    return result .. category
end

return p

このモジュールは、記事空間とトーク空間でのみカテゴリ分けを行います。ただし、/archive のようなページではカテゴリ分けされません(ブラックリスト参照)。テンプレートを議論ページで例示したい場合などは、nocat=true を渡してカテゴリ分けを抑制できます。たとえば:

== 私の新しいモジュール ==
皆さん、この新しいモジュールを見ましたか?
{{#invoke:mymodule|main|nocat=true}}
すごいでしょ?
--~~~~

複数の名前空間で同じカテゴリを使用したい場合は、次のように記述します:

p = {}
local categoryHandler = require( 'Module:Category handler' ).main

function p.main( frame )
    local result = 'このモジュールはいくつかの名前空間で使用されます。'
    local category = categoryHandler{
        main = '[[Category:Somecat1]]',
        [ 1 ] = '[[Category:Somecat2]]', -- ヘルプ と 利用者 空間用
        help = 1,
        user = 1,
        talk = '', -- 議論ページではカテゴリ分けしない
        other = '[[Category:Somecat3]]', -- その他すべての空間で使用
        nocat = frame.args.nocat
    }
    category = category or ''
    return result .. category
end

return p

この例では、数値付きパラメータ(ここでは `1`)を使用してカテゴリを指定し、それを ヘルプと利用者空間で使うように指示しています。

このモジュールは、無制限の数の数値パラメータに対応しています。

other パラメータは、明示的に指定されていない名前空間で使用されるカテゴリや出力を指定します。

talk パラメータが空(定義されているが中身がない)であることに注意してください。これにより、議論ページであっても other に指定された内容が表示されないようになります。

このモジュールには、all というパラメータもあります。次のように使用できます:

p = {}
local categoryHandler = require( 'Module:Category handler' ).main

function p.main( frame )
    local result = 'このモジュールはすべての名前空間で使用されます。'
    local category = categoryHandler{
        all = '[[Category:Somecat1]]', -- すべての名前空間でカテゴリ分け
        nocat = frame.args.nocat
    }
    category = category or ''
    return result .. category
end

return p

この例では、すべての名前空間でカテゴリが追加されます。ただし、ブラックリストに載っているページでは追加されません。テンプレートを例示する場合などには、nocat=true を使ってカテゴリ分けを抑制してください。

all パラメータの使用は避けるのが望ましいです。テンプレートやモジュールは、必要な名前空間でのみカテゴリ分けを行うのが理想的だからです。

ただし、all パラメータは他のパラメータと組み合わせて使用することもできます。次のように:

p = {}
local categoryHandler = require( 'Module:Category handler' ).main

function p.main( frame )
    local result = 'このモジュールはすべての名前空間で使用されます。'
    local category = categoryHandler{
        all = '[[Category:Somecat1]]', -- すべての空間で追加
        main = '[[Category:Somecat2]]', -- 記事空間に追加で追加
        other = '[[Category:Somecat3]]', -- その他の空間で追加で追加
        nocat = frame.args.nocat
    }
    category = category or ''
    return result .. category
end

return p

このコードを記事ページに置いた場合、「Somecat1」と「Somecat2」の両方が追加されます。他の名前空間では「Somecat1」と「Somecat3」が追加されます。all パラメータは、他のパラメータと独立して動作します。

サブページ

[編集]

このカテゴリハンドラーモジュールは、subpage パラメータに対応しています。以下のように使用します:

p = {}
local categoryHandler = require( 'Module:Category handler' ).main

function p.main( frame )
    local result = 'このモジュールはすべての名前空間で使用されます。'
    local category = categoryHandler{
        subpage = 'no', -- サブページではカテゴリ分けしない
        wikipedia = '[[Category:Somecat]]',
        nocat = frame.args.nocat -- "nocat=true/false" に対応
    }
    category = category or ''
    return result .. category
end

return p

subpage = 'no' を指定すると、このテンプレートはサブページ上ではカテゴリ分けを行いません。 逆に、サブページのみでカテゴリ分けを行いたい場合は、subpage = 'only' を使います。 subpage が空または未定義の場合は、通常どおりベースページおよびサブページの両方でカテゴリ分けを行います。

ブラックリスト

[編集]

このモジュールには、テンプレートが自動でカテゴリ分けをすべきでないページやページ種別のブラックリストがあります。 そのため、このメタテンプレートを使用するモジュールは、たとえば `/archive` ページや Wikipedia:Template messages のサブページなどではカテゴリを追加しません。

ブラックリスト上のページでもテンプレートにカテゴリを追加させたい場合は、nocat = false を指定することでブラックリストチェックを無視できます。

ただし、このモジュールは、指定された名前空間に対するカテゴリデータが存在する場合にのみカテゴリ分けを行います。 たとえば基本構文を使用している場合(#基本的な使い方参照)、nocat = false を指定しても、議論ページ向けのカテゴリが指定されていなければカテゴリ分けは行われません。 一方、たとえば ヘルプ空間用のカテゴリが指定されていれば、ブラックリストに載っている help ページ上でもカテゴリが追加されます。

ブラックリストは、モジュールコード上部にある構成テーブル cfg.blacklist に定義されています。

カテゴリとテキスト

[編集]

このモジュールは、カテゴリだけでなく、任意のテキストも出力できます。たとえば次のように:

p = {}
local categoryHandler = require( 'Module:Category handler' ).main

function p.main( frame )
    local result = 'このモジュールは会話ページで使用されます。'
    local category = categoryHandler{
        talk = '[[Category:Somecat]]',
        other = '<p class="error">このモジュールは会話ページでのみ使用すべきです。</p>',
        nocat = frame.args.nocat -- "nocat=true/false" に対応
    }
    category = category or ''
    return result .. category
end

return p

上記のモジュールコードを会話ページ以外で使用した場合、以下のように出力されます:

このモジュールは会話ページで使用されます。

このモジュールは会話ページでのみ使用すべきです。

ただし、この方法で表示されるテキストはブラックリスト対象ページでは出力されないため、重要な情報を伝える目的には使用しないでください。 nocat = 'true' を渡すと、カテゴリ同様にテキストも非表示になります。

page パラメータ

[編集]

テストやデモ用に、このモジュールは page というパラメータを受け取ることができます。使用例:

p = {}
local categoryHandler = require( 'Module:Category handler' ).main

function p.main( frame )
    local category = categoryHandler{
        main = 'Category:Some cat',
        talk = 'Category:Talk cat',
        nocat = frame.args.nocat, -- "nocat=true/false" に対応
        page = 'User talk:Example'
    }
    return category
end

return p

上記のコードでは、カテゴリ名の角括弧([[ ]])をあえて外しているため、実際のページ上で出力が確認できます。 このコードは、どのページで使われても、次のような出力を返します:

page パラメータを指定すると、そのページにいるかのようにこのモジュールが動作します。ブラックリストチェックも機能します。 指定されたページ名は、実在するページでなくても構いません。

自分のモジュールでも page パラメータを受け取れるようにすれば、実際に他のページを編集せずに、別ページ上でどうカテゴリ分けされるかをテストできます。例:

p = {}
local categoryHandler = require( 'Module:Category handler' ).main

function p.main( frame )
    local category = categoryHandler{
        main = 'Category:Some cat',
        talk = 'Category:Talk cat',
        nocat = frame.args.nocat, -- "nocat=true/false" に対応
        page = frame.args.page -- テスト用
    }
    return category
end

return p

パラメータ一覧

[編集]

利用可能なすべてのパラメータ:

  • 最初の位置引数 — デフォルト設定用
  • subpage = 'no' / 'only'
  • 1, 2, 3 ...(数値指定の引数)
  • all = または '任意のテキスト'
  • main = 1, 2, 3 ... / / '任意のテキスト'
  • user, wikipedia, file, template, help, category, portal, book, draft, education program, timedtext, module, topic, mediawiki, gadget, gadget definition(などの名前空間名) = 同様に設定可
  • other = 同上(未指定の名前空間用)
  • nocat = frame.args.nocat / true / false / 'yes' / 'no' / 'y' / 'n' / 'true' / 'false' / 1 / 0
  • categories = 同上(後方互換)
  • category2 = frame.args.category または '¬' / 'no' / 未定義 / 'yes'
  • page = frame.args.page または 'User:Example'

注意:mainother の各パラメータに空の値を設定することには特別な意味があります(例を参照)。 all パラメータでは数値引数(1, 2, 3, …)は使用できません。使用の必要がないためです。

他のウィキへのエクスポート

[編集]

このモジュールは、構成テーブル cfg 内の設定値を変更することで、他のウィキへ移植できます。 すべての変数値は構成可能になっており、設定が完了すればモジュール本体のコードを変更する必要はありません。 各設定項目の詳細は、モジュールコード中のコメントに記載されています。

また、このモジュールを動作させるには、Module:Namespace detect がローカルのウィキ上に存在する必要があります。

関連項目

[編集]

--------------------------------------------------------------------------------
--                                                                            --
--                              CATEGORY HANDLER                              --
--                                                                            --
--      This module implements the {{category handler}} template in Lua,      --
--      with a few improvements: all namespaces and all namespace aliases     --
--      are supported, and namespace names are detected automatically for     --
--      the local wiki. This module requires [[Module:Namespace detect]]      --
--      and [[Module:Yesno]] to be available on the local wiki. It can be     --
--      configured for different wikis by altering the values in              --
--      [[Module:Category handler/config]], and pages can be blacklisted      --
--      from categorisation by using [[Module:Category handler/blacklist]].   --
--                                                                            --
--------------------------------------------------------------------------------

-- Load required modules
local yesno = require('Module:Yesno')

-- Lazily load things we don't always need
local mShared, mappings

local p = {}

--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------

local function trimWhitespace(s, removeBlanks)
	if type(s) ~= 'string' then
		return s
	end
	s = s:match('^%s*(.-)%s*$')
	if removeBlanks then
		if s ~= '' then
			return s
		else
			return nil
		end
	else
		return s
	end
end

--------------------------------------------------------------------------------
-- CategoryHandler class
--------------------------------------------------------------------------------

local CategoryHandler = {}
CategoryHandler.__index = CategoryHandler

function CategoryHandler.new(data, args)
	local obj = setmetatable({ _data = data, _args = args }, CategoryHandler)
	
	-- Set the title object
	do
		local pagename = obj:parameter('demopage')
		local success, titleObj
		if pagename then
			success, titleObj = pcall(mw.title.new, pagename)
		end
		if success and titleObj then
			obj.title = titleObj
			if titleObj == mw.title.getCurrentTitle() then
				obj._usesCurrentTitle = true
			end
		else
			obj.title = mw.title.getCurrentTitle()
			obj._usesCurrentTitle = true
		end
	end

	-- Set suppression parameter values
	for _, key in ipairs{'nocat', 'categories'} do
		local value = obj:parameter(key)
		value = trimWhitespace(value, true)
		obj['_' .. key] = yesno(value)
	end
	do
		local subpage = obj:parameter('subpage')
		local category2 = obj:parameter('category2')
		if type(subpage) == 'string' then
			subpage = mw.ustring.lower(subpage)
		end
		if type(category2) == 'string' then
			subpage = mw.ustring.lower(category2)
		end
		obj._subpage = trimWhitespace(subpage, true)
		obj._category2 = trimWhitespace(category2) -- don't remove blank values
	end
	return obj
end

function CategoryHandler:parameter(key)
	local parameterNames = self._data.parameters[key]
	local pntype = type(parameterNames)
	if pntype == 'string' or pntype == 'number' then
		return self._args[parameterNames]
	elseif pntype == 'table' then
		for _, name in ipairs(parameterNames) do
			local value = self._args[name]
			if value ~= nil then
				return value
			end
		end
		return nil
	else
		error(string.format(
			'invalid config key "%s"',
			tostring(key)
		), 2)
	end
end

function CategoryHandler:isSuppressedByArguments()
	return
		-- See if a category suppression argument has been set.
		self._nocat == true
		or self._categories == false
		or (
			self._category2
			and self._category2 ~= self._data.category2Yes
			and self._category2 ~= self._data.category2Negative
		)

		-- Check whether we are on a subpage, and see if categories are
		-- suppressed based on our subpage status.
		or self._subpage == self._data.subpageNo and self.title.isSubpage
		or self._subpage == self._data.subpageOnly and not self.title.isSubpage
end

function CategoryHandler:shouldSkipBlacklistCheck()
	-- Check whether the category suppression arguments indicate we
	-- should skip the blacklist check.
	return self._nocat == false
		or self._categories == true
		or self._category2 == self._data.category2Yes
end

function CategoryHandler:matchesBlacklist()
	if self._usesCurrentTitle then
		return self._data.currentTitleMatchesBlacklist
	else
		mShared = mShared or require('Module:Category handler/shared')
		return mShared.matchesBlacklist(
			self.title.prefixedText,
			mw.loadData('Module:Category handler/blacklist')
		)
	end
end

function CategoryHandler:isSuppressed()
	-- Find if categories are suppressed by either the arguments or by
	-- matching the blacklist.
	return self:isSuppressedByArguments()
		or not self:shouldSkipBlacklistCheck() and self:matchesBlacklist()
end

function CategoryHandler:getNamespaceParameters()
	if self._usesCurrentTitle then
		return self._data.currentTitleNamespaceParameters
	else
		if not mappings then
			mShared = mShared or require('Module:Category handler/shared')
			mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData
		end
		return mShared.getNamespaceParameters(
			self.title,
			mappings
		)
	end
end

function CategoryHandler:namespaceParametersExist()
	-- Find whether any namespace parameters have been specified.
	-- We use the order "all" --> namespace params --> "other" as this is what
	-- the old template did.
	if self:parameter('all') then
		return true
	end
	if not mappings then
		mShared = mShared or require('Module:Category handler/shared')
		mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData
	end
	for ns, params in pairs(mappings) do
		for i, param in ipairs(params) do
			if self._args[param] then
				return true
			end
		end
	end
	if self:parameter('other') then
		return true
	end
	return false
end

function CategoryHandler:getCategories()
	local params = self:getNamespaceParameters()
	local nsCategory
	for i, param in ipairs(params) do
		local value = self._args[param]
		if value ~= nil then
			nsCategory = value
			break
		end
	end
	if nsCategory ~= nil or self:namespaceParametersExist() then
		-- Namespace parameters exist - advanced usage.
		if nsCategory == nil then
			nsCategory = self:parameter('other')
		end
		local ret = {self:parameter('all')}
		local numParam = tonumber(nsCategory)
		if numParam and numParam >= 1 and math.floor(numParam) == numParam then
			-- nsCategory is an integer
			ret[#ret + 1] = self._args[numParam]
		else
			ret[#ret + 1] = nsCategory
		end
		if #ret < 1 then
			return nil
		else
			return table.concat(ret)
		end
	elseif self._data.defaultNamespaces[self.title.namespace] then
		-- Namespace parameters don't exist, simple usage.
		return self._args[1]
	end
	return nil
end

--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------

local p = {}

function p._exportClasses()
	-- Used for testing purposes.
	return {
		CategoryHandler = CategoryHandler
	}
end

function p._main(args, data)
	data = data or mw.loadData('Module:Category handler/data')
	local handler = CategoryHandler.new(data, args)
	if handler:isSuppressed() then
		return nil
	end
	return handler:getCategories()
end

function p.main(frame, data)
	data = data or mw.loadData('Module:Category handler/data')
	local args = require('Module:Arguments').getArgs(frame, {
		wrappers = data.wrappers,
		valueFunc = function (k, v)
			v = trimWhitespace(v)
			if type(k) == 'number' then
				if v ~= '' then
					return v
				else
					return nil
				end
			else
				return v
			end
		end
	})
	return p._main(args, data)
end

return p